using B.S.Medical.Api.Read.Application.Command.DoctorSchedule;
using B.S.Medical.Api.Read.DTOS;
using B.S.Medical.Domain.Medical;
using B.S.Medical.Domain.Doctor;
using B.S.Medical.Domain.Department;
using B.S.Medical.ErrorCode;
using B.S.Medical.Infrastructure.Interface;
using MediatR;

namespace B.S.Medical.Api.Read.Application.Handler.DoctorSchedule
{
    /// <summary>
    /// 医生排班分页查询Handler，实现按医生分组一周排班的分页查询
    /// </summary>
    public class GetDoctorScheduleHandler : IRequestHandler<GetDoctorScheduleCommand, ApiPageing<DoctorScheduleDto>>
    {
        private readonly IBaseRepository<DoctorScheduleModel> _scheduleRepo;
        private readonly IBaseRepository<DoctorModel> _doctorRepo;
        private readonly IBaseRepository<ManageModel> _departmentRepo;
        private readonly IBaseRepository<RegisterModel> _registerRepo;

        /// <summary>
        /// 构造方法，注入所需仓储
        /// </summary>
        public GetDoctorScheduleHandler(
            IBaseRepository<DoctorScheduleModel> scheduleRepo,
            IBaseRepository<DoctorModel> doctorRepo,
            IBaseRepository<ManageModel> departmentRepo,
            IBaseRepository<RegisterModel> registerRepo)
        {
            _scheduleRepo = scheduleRepo;
            _doctorRepo = doctorRepo;
            _departmentRepo = departmentRepo;
            _registerRepo = registerRepo;
        }

        /// <summary>
        /// 处理分页查询请求，返回医生一周排班分页数据
        /// </summary>
        public async Task<ApiPageing<DoctorScheduleDto>> Handle(GetDoctorScheduleCommand request, CancellationToken cancellationToken)
        {
            // 1. 获取一周的日期范围
            var weekStart = request.WeekStart ?? DateTime.Today;
            var weekEnd = request.WeekEnd ?? weekStart.AddDays(6);
            var weekDates = Enumerable.Range(0, (weekEnd - weekStart).Days + 1)
                .Select(i => weekStart.AddDays(i)).ToList();

            // 2. 查询医生（分页）
            // 支持科室、姓名筛选，仅查有效医生
            var doctorQuery = _doctorRepo.GetAll()
                .Where(d => d.Status) // 只查状态为true的医生
                .Where(d => !request.DepartmentId.HasValue || d.DepartmentId == request.DepartmentId.Value)
                .Where(d => string.IsNullOrEmpty(request.DoctorName) || d.DoctorName.Contains(request.DoctorName));
            var totalCount = doctorQuery.Count();
            var doctorPage = doctorQuery
                .OrderBy(d => d.DoctorName)
                .Skip((request.PageIndex - 1) * request.PageSize)
                .Take(request.PageSize)
                .ToList();
            var doctorIds = doctorPage.Select(d => d.Id).ToList();

            // 3. 查询这些医生一周内的排班数据
            var scheduleList = _scheduleRepo.GetAll()
                .Where(s => doctorIds.Contains(s.DoctorId))
                .Where(s => s.ScheduleDate >= weekStart && s.ScheduleDate <= weekEnd)
                .ToList();

            // 4. 查询科室信息，组装字典便于后续查找
            var departmentIds = doctorPage.Select(d => d.DepartmentId).Distinct().ToList();
            var departmentDict = _departmentRepo.GetAll()
                .Where(dep => departmentIds.Contains(dep.Id))
                .ToDictionary(dep => dep.Id, dep => dep.DepartmentName);

            // 5. 查询预约信息（RegisterModel），只统计有效预约（如：待就诊、已就诊）
            var validStatus = new[] { "待就诊", "已就诊" };
            var registerList = _registerRepo.GetAll()
                .Where(r => doctorIds.Contains(r.DoctorId))
                .Where(r => r.VisitDate >= weekStart && r.VisitDate <= weekEnd)
                .Where(r => validStatus.Contains(r.Status))
                .ToList();

            // 6. 预约分组统计（上午/下午，ReserveTime小于12点为上午，12点及以后为下午）
            var registerDict = registerList
                .GroupBy(r => new { r.DoctorId, Date = r.AppointmentTime.Date, Period = r.AppointmentTime.Hour < 12 ? 1 : 2 })
                .ToDictionary(
                    g => (g.Key.DoctorId, g.Key.Date, g.Key.Period),
                    g => g.Count()
                );

            // 7. 组装前端需要的结构：每个医生一周每天上午/下午的排班
            var result = new List<DoctorScheduleDto>();
            foreach (var doctor in doctorPage)
            {
                foreach (var date in weekDates)
                {
                    // 上午排班
                    var am = scheduleList.FirstOrDefault(x => x.DoctorId == doctor.Id && x.ScheduleDate == date && x.Period == 1);
                    var amKey = (doctor.Id, date.Date, 1);
                    // 下午排班
                    var pm = scheduleList.FirstOrDefault(x => x.DoctorId == doctor.Id && x.ScheduleDate == date && x.Period == 2);
                    var pmKey = (doctor.Id, date.Date, 2);

                    // 上午数据
                    result.Add(new DoctorScheduleDto
                    {
                        DoctorId = doctor.Id,
                        DoctorName = doctor.DoctorName,
                        DepartmentId = doctor.DepartmentId,
                        DepartmentName = departmentDict.ContainsKey(doctor.DepartmentId) ? departmentDict[doctor.DepartmentId] : string.Empty,
                        ScheduleDate = date,
                        Period = 1,
                        IsAvailable = am?.IsAvailable ?? false,
                        SlotCount = am?.SlotCount ?? 0,
                        StartTime = am?.StartTime,
                        EndTime = am?.EndTime,
                        AppointmentCount = registerDict.TryGetValue(amKey, out var amCount) ? amCount : 0
                    });
                    // 下午数据
                    result.Add(new DoctorScheduleDto
                    {
                        DoctorId = doctor.Id,
                        DoctorName = doctor.DoctorName,
                        DepartmentId = doctor.DepartmentId,
                        DepartmentName = departmentDict.ContainsKey(doctor.DepartmentId) ? departmentDict[doctor.DepartmentId] : string.Empty,
                        ScheduleDate = date,
                        Period = 2,
                        IsAvailable = pm?.IsAvailable ?? false,
                        StartTime = pm?.StartTime,
                        EndTime = pm?.EndTime,
                        SlotCount = pm?.SlotCount ?? 0,
                        AppointmentCount = registerDict.TryGetValue(pmKey, out var pmCount) ? pmCount : 0
                    });
                }
            }

            // 8. 返回分页结构
            return new ApiPageing<DoctorScheduleDto>
            {
                TotaCount = totalCount,
                TotaPage = (int)Math.Ceiling(totalCount / (double)request.PageSize),
                PageData = result
            };
        }
    }
} 